#!/usr/bin/perl

use strict;
use warnings;
use feature 'say';


#requires: sudo apt install libauthen-simple-pam-perl
use Authen::Simple::PAM;

# syslog
use Sys::Syslog qw(:DEFAULT setlogsock);
use File::Basename;
setlogsock("unix");
openlog(basename($0), "pid", "auth");

# tigervncserver  executable
my $exe="/usr/bin/tigervncserver" ;

# check if running as root
my $login = (getpwuid $>);
die "Not running as root, as required." if $login ne 'root';

# get a username from the first argument, if provided
my $username = shift @ARGV ;
## DO NOT # use the username under which the current script is running
##        $username or $username = getpwuid($<);
$username or die "Empty username";

# get the source ip address from the first argument, if provided
my $sourceip = shift @ARGV ;

# get the further users to share the session with
my $sharewith = join(',',"@ARGV" =~ /([a-z_][a-z0-9_-]{0,31})/g);


# check whether the username is plausible
$username =~ /^[a-z_][a-z0-9_-]{0,31}$/ or die "Invalid username." ;

# get the password from STDIN (to avoid having it logged..)
my $password = <STDIN> ;
chomp $password ;

$password or die "Empty password";

# check credentials against the pam login service 
my $pam = Authen::Simple::PAM->new( service => 'login');
if ( $pam->authenticate( $username, $password ) ) {
	syslog("notice", "Accepted credentials for user %s from %s .", $username, $sourceip);
} else {
	syslog("notice", "Failed credentials for user %s from %s .", $username, $sourceip);
	die "Unable to authenticate with the provided credentials.";
}

# the user is now identified. Get basic info about him. 
(my $uid, my $home)=(getpwnam($username))[2,7];
$uid != 0 or die "Error in getting user UID.";

# also create the home directory, if it does not exist
if ( ! -d $home ) {
	say STDERR "Missing home folder, let's create $home .";
	system('mkhomedir_helper', $username) == 0 or die "Unable to create a home folder";
	say STDERR "Done.";
}

# the user is now identified. Let's impersonate him.
POSIX::setuid($uid) or die "Error in acting in behalf of the user.";

# set the home directory environment variable 
$ENV{'HOME'} = $home;
$ENV{'LOGNAME'} = $username;
$ENV{'USER'} = $username;
$ENV{'MAIL'} = '/var/mail/$username';

# and move there
chdir "$home" or die "Error in accessing user home directory.";

# Root privileges are lost.
# The code below is run as the user.

# Check if a running vnc session exists for the user
my $out = `$exe --list`;
foreach my $line (split(/\n/, $out)) {
	my @grp = $line =~ m/^(:[0-9]+)\s+([0-9]+)\s+([0-9]+)\s+([0-9a-zA-Z_-]+)$/i ;
	if ( @grp ) {
		my $port=$grp[1];
		say STDERR "Found a Xvnc session for the user at port $port .";
		# print to STDOUT the port to which the user's Xvnc server is listenting to
		say "$port";
		exit; # EXITS FROM THE SCRIPT!!
	}
}

# otherwise, start a new session in the first available port
$out = `tigervncserver -geometry 800x600 -PAMService login -PlainUsers $username,$sharewith -SecurityTypes Plain -localhost` ;

foreach my $line (split(/\n/, $out)) {
	my @grp = $out =~ m/ on port ([0-9]+) for display :/ ;
	if ( @grp ) {
		my $port=$grp[0];
		say STDERR "Just opened a Xvnc session for the user $username at port $port .";
		# print to STDOUT the port to which the user's Xvnc server is listenting to
		say "$port";
		say STDERR "You may share it with users: $sharewith";
		exit; # EXITS FROM THE SCRIPT!!
	}
}

# something went wrong..
closelog( );
die "Unable to find or open an Xvnc server session. Exiting." ;
